package com.xingchi.tornado.mybatisplus.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.xingchi.tornado.model.BaseDO;
import com.xingchi.tornado.model.BaseDTO;
import com.xingchi.tornado.model.PageResult;
import com.xingchi.tornado.model.PaginationQuery;
import com.xingchi.tornado.utils.BeanUtils;
import com.xingchi.tornado.utils.GenericTypeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 基于mybatisplus的抽象service实现
 *
 * @author xingchi
 * @date 2024/3/2 19:25
 */
public abstract class AbstractBasePlusService<DAO extends BaseMapper<DO>, DTO extends BaseDTO, Q extends PaginationQuery, DO extends BaseDO> extends ServiceImpl<DAO, DO> implements BasePlusService<DTO, Q> {

    private static final Logger log = LoggerFactory.getLogger(AbstractBasePlusService.class);

    /**
     * 默认批处理大小
     */
    protected static final int DEFAULT_BATCH_SIZE = 1000;

    /**
     * 缓存名称前缀
     */
    protected static final String CACHE_PREFIX = "tornado:mybatis-plus:";

    @Autowired
    protected DAO mapper;

    protected DAO getDAO() {
        return mapper;
    }

    public abstract Wrapper<DO> getWrapper(Q query);

    @Override
    public Long count(Q query) {
        return getDAO().selectCount(getWrapper(query));
    }

    @Override
    public List<DTO> selectByQuery(Q query) {
        final Wrapper<DO> wrapper = getWrapper(query);

        if (!query.isPagination()) {
            return fromDO2DTO(getDAO().selectList(wrapper));
        }
        return fromDO2DTO(getDAO().selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper).getRecords());
    }

    @Override
    public PageResult<DTO> selectPageByQuery(Q query) {

        final Wrapper<DO> wrapper = getWrapper(query);
        if (query.isPagination()) {
            IPage<DO> records = getDAO().selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper);
            return PageResult.fetchPage(records, this::fromDO2DTO);
        }

        List<DTO> ts = fromDO2DTO(getDAO().selectList(wrapper));
        return PageResult.fetchPage(new Page<>(query.getPageNum(), ts.size(), ts.size()));
    }

    @Override
    public DTO selectOne(Q query) {
        final Wrapper<DO> wrapper = getWrapper(query);
        Page<DO> result = getDAO().selectPage(new Page<>(1, 1, false), wrapper);
        return fromDO2DTO(result.getRecords().stream().findFirst().orElse(null));
    }

    @Override
    public DTO insert(DTO record) {
        // 验证参数
        check(BASE_OP.INSERT, record);
        DO basePO = fromDTO2DO(record);
        getDAO().insert(basePO);
        record.setId(basePO.getId());

        return record;
    }

    @Override
    public Integer deleteByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        return getDAO().deleteByIds(ids);
    }

    @Override
    public boolean exists(Long id) {
        return this.selectById(id) != null;
    }

    @Override
    public boolean exists(Q query) {
        return this.selectOne(query) != null;
    }

    @Override
    public boolean check(BASE_OP baseOp, DTO baseDTO) {
        return true;
    }

    @Override
    public DTO saveOrUpdate(DTO record) {
        if (isUpdate(record)) {
            updateById(record);
            return record;
        }
        return insert(record);
    }

    @Override
    public List<Long> selectIds(Q query) {
        Wrapper<DO> wrapper = this.getWrapper(query);
        if (wrapper instanceof Query) {
            Class<DO> superClassGenericType = (Class<DO>) GenericTypeUtils.getSuperClassGenericType(getDAO().getClass(), BaseMapper.class, 0);
            ((Query<?, DO, ?>) wrapper).select(superClassGenericType, item -> false);
        }
        return getDAO().selectList(wrapper).stream().map(BaseDO::getId).collect(Collectors.toList());
    }

    /**
     * 批量新增
     *
     * @param records 批量新增记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveBatch(List<DTO> records) {
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        this.saveBatch(fromDTO2DO(records), DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量新增，支持自定义批次大小
     *
     * @param records 批量新增记录
     * @param batchSize 每批处理的记录数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveBatch(List<DTO> records, int batchSize) {
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        this.saveBatch(fromDTO2DO(records), batchSize);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(List<DTO> records) {
        return this.updateBatchById(this.fromDTO2DO(records), DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量更新，支持自定义批次大小
     *
     * @param records 批量更新记录
     * @param batchSize 每批处理的记录数
     * @return 是否更新成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(List<DTO> records, int batchSize) {
        return this.updateBatchById(this.fromDTO2DO(records), batchSize);
    }

    @Override
    public List<DTO> selectByIds(Collection<Long> ids) {
        List<DO> list = getDAO().selectBatchIds(ids);
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        return fromDO2DTO(list);
    }

    protected boolean isUpdate(DTO record) {
        return record != null && record.getId() != null;
    }

    public List<DTO> fromDO2DTO(List<DO> list) {

        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }

        List<DTO> result = new ArrayList<>();
        for (DO baseDO : list) {
            DTO dto = fromDO2DTO(baseDO);
            if (dto != null) {
                result.add(dto);
            }
        }

        return result;
    }

    public List<DO> fromDTO2DO(List<DTO> list) {

        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }

        List<DO> result = new ArrayList<>();
        for (DTO baseDTO : list) {
            DO dto = fromDTO2DO(baseDTO);
            if (dto != null) {
                result.add(dto);
            }
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    public DTO fromDO2DTO(BaseDO baseDO) {
        return fromDO2DTO(baseDO, (DTO) GenericTypeUtils.createInstance(this.getClass(), BaseService.class, 0));
    }

    @SuppressWarnings("unchecked")
    public DO fromDTO2DO(DTO baseDTO) {
        if (baseDTO == null) {
            return null;
        }

        return (DO) BeanUtils.convert(baseDTO, GenericTypeUtils.getSuperClassGenericType(getDAO().getClass(), BaseMapper.class, 0));
    }

    public DTO fromDO2DTO(BaseDO baseDO, DTO baseDTO) {
        if (baseDO == null) {
            return null;
        }
        BeanUtils.convert(baseDO, baseDTO);
        return baseDTO;
    }

    /**
     * 获取实体缓存名称
     * 子类可以覆盖此方法自定义缓存名称
     *
     * @return 缓存名称
     */
    protected String getCacheName() {
        try {
            Class<DO> entityClass = (Class<DO>) GenericTypeUtils.getSuperClassGenericType(
                    getDAO().getClass(), BaseMapper.class, 0);
            return CACHE_PREFIX + entityClass.getSimpleName().toLowerCase();
        } catch (Exception e) {
            log.warn("获取缓存名称失败，将使用默认缓存名称", e);
            return CACHE_PREFIX + "entity";
        }
    }

    /**
     * 获取实体缓存键
     *
     * @param id 实体ID
     * @return 缓存键
     */
    protected String getCacheKey(Long id) {
        return getCacheName() + ":" + id;
    }

    /**
     * 清除指定实体的缓存
     *
     * @param id 实体ID
     */
    protected void clearCache(Long id) {
        // 这里可以实现自定义缓存清除逻辑
        log.debug("清除缓存: {}", getCacheKey(id));
    }

    /**
     * 清除所有缓存
     */
    protected void clearAllCache() {
        // 这里可以实现自定义缓存清除逻辑
        log.debug("清除所有缓存: {}", getCacheName());
    }

    @Override
    public DTO selectById(Long id) {
        return fromDO2DTO(getDAO().selectById(id));
    }

    @Override
    public Boolean updateById(DTO record) {
        check(BASE_OP.UPDATE, record);
        return getDAO().updateById(fromDTO2DO(record)) > 0;
    }

    @Override
    public Boolean deleteById(Long id) {
        if (id == null) {
            return false;
        }
        return this.removeById(id);
    }

}
